#include "ffmpeg/tool_ffmpeg.h"
#include <cstdlib>
#include <opencv2/opencv.hpp>

extern "C"
{
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/error.h>
#include <libavutil/imgutils.h>
}

namespace tool::ffmpeg
{
    ToolFfmpeg::ToolFfmpeg() {}
    ToolFfmpeg::~ToolFfmpeg() {}
    void ToolFfmpeg::ConvertY4mToYuv(const std::string &y4mFilePath, const std::string &yuvDir)
    {
        std::cout << "ConvertY4mToYuv from:" << y4mFilePath << "; to yuv:" << yuvDir << std::endl;
        AVFormatContext *input_format_context = nullptr;
        // 打开输入文件
        auto ret_open = avformat_open_input(&input_format_context, y4mFilePath.c_str(), NULL, NULL);
        if (ret_open != 0)
        {
            char errbuf[AV_ERROR_MAX_STRING_SIZE];
            av_strerror(ret_open, errbuf, AV_ERROR_MAX_STRING_SIZE);
            std::cerr << "fail open y4m:" << y4mFilePath << ";msg:" << errbuf << std::endl;
            return;
        }
        // 查找视频流信息
        if (avformat_find_stream_info(input_format_context, nullptr) < 0)
        {
            std::cerr << "Failed to retrieve input stream information" << std::endl;
            avformat_close_input(&input_format_context);
            return;
        }
        // 查找视频流索引
        int video_stream_index = -1;
        for (int i = 0; i < input_format_context->nb_streams; i++)
        {
            if (input_format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                video_stream_index = i;
                break;
            }
        }
        if (video_stream_index == -1)
        {
            std::cerr << "Failed to find video stream" << std::endl;
            avformat_close_input(&input_format_context);
            return;
        }

        // 从流中获取解码器参数
        AVCodecParameters *codec_params = input_format_context->streams[video_stream_index]->codecpar;
        // 查找解码器
        const AVCodec *p_avcodec = avcodec_find_decoder(codec_params->codec_id);
        if (!p_avcodec)
        {
            std::cerr << "fail get avcodec y4m:" << y4mFilePath << std::endl;
            return;
        }
        // 创建视频解码上下文
        AVCodecContext *avcodec_context = avcodec_alloc_context3(p_avcodec);
        if (!avcodec_context)
        {
            std::cerr << "fail get avcodec context y4m:" << y4mFilePath << std::endl;
            avformat_close_input(&input_format_context);
            return;
        }
        // 将解码器参数复制到解码器上下文
        if (avcodec_parameters_to_context(avcodec_context, codec_params) < 0)
        {
            std::cerr << "Failed to copy codec parameters to codec context" << std::endl;
            avformat_close_input(&input_format_context);
            avcodec_free_context(&avcodec_context);
            return;
        }
        // 打开解码器
        if (avcodec_open2(avcodec_context, p_avcodec, NULL) < 0)
        {
            std::cerr << "fail open avcodec y4m:" << y4mFilePath << std::endl;
            avformat_close_input(&input_format_context);
            avcodec_free_context(&avcodec_context);
            return;
        }
        // 打印输入视频信息
        std::cout << "Input file information:" << std::endl;
        av_dump_format(input_format_context, 0, y4mFilePath.c_str(), 0);
        // 创建视频帧
        AVFrame *p_frame = av_frame_alloc();
        if (!p_frame)
        {
            std::cerr << "Fail create video frame y4m:" << y4mFilePath << std::endl;
            avformat_close_input(&input_format_context);
            avcodec_free_context(&avcodec_context);
            return;
        }
        // 创建像素格式转换上下文
        int video_width = avcodec_context->width;
        int video_height = avcodec_context->height;
        SwsContext *sws_context = sws_getContext(video_width, video_height, avcodec_context->pix_fmt,
                                                 video_width, video_height, AV_PIX_FMT_YUV420P,
                                                 SWS_BILINEAR, NULL, NULL, NULL);
        if (!sws_context)
        {
            std::cerr << "Failed to create pixel format conversion context" << std::endl;
            av_frame_free(&p_frame);
            avformat_close_input(&input_format_context);
            avcodec_free_context(&avcodec_context);
            return;
        }
        // 读取并解码视频帧
        int yuv_index = 0;
        AVPacket packet;
        av_init_packet(&packet);
        packet.data = nullptr;
        packet.size = 0;
        while (av_read_frame(input_format_context, &packet) >= 0)
        {
            if (packet.stream_index == video_stream_index)
            {
                int ret = avcodec_send_packet(avcodec_context, &packet);
                if (ret < 0)
                {
                    std::cerr << "Error sending a packet for decoding" << std::endl;
                    break;
                }
                while (ret >= 0)
                {
                    ret = avcodec_receive_frame(avcodec_context, p_frame);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                    {
                        std::cout << "read frame done." << std::endl;
                        break;
                    }
                    else if (ret < 0)
                    {
                        std::cerr << "fail read frame during decoding" << std::endl;
                        break;
                    }
                    // 将帧转换为目标像素格式
                    sws_scale(sws_context, p_frame->data, p_frame->linesize, 0, video_height, p_frame->data, p_frame->linesize);
                    // 写入文件
                    yuv_index++;
                    std::cout << "p_frame->data:" << (p_frame->data == nullptr) << std::endl;
                    char yuv_output_path[100];
                    sprintf(yuv_output_path, "%s/yuv_%d.yuv", yuvDir.c_str(), yuv_index);
                    std::cout << "yuv out:" << yuv_output_path << ";width:" << video_width << ";height:" << video_height << std::endl;
                    FILE *yuv_file = fopen(yuv_output_path, "wb");
                    if (yuv_file)
                    {
                        for (int i = 0; i < video_height; i++)
                        {
                            fwrite(p_frame->data[0] + i * p_frame->linesize[0], 1, video_width, yuv_file);
                        }
                        for (int i = 0; i < video_height / 2; i++)
                        {
                            fwrite(p_frame->data[1] + i * p_frame->linesize[1], 1, video_width / 2, yuv_file);
                        }
                        for (int i = 0; i < video_height / 2; i++)
                        {
                            fwrite(p_frame->data[2] + i * p_frame->linesize[2], 1, video_width / 2, yuv_file);
                        }
                        fclose(yuv_file);
                        std::cout << "success save to yuv:" << yuv_output_path << std::endl;
                    }
                    else
                    {
                        std::cerr << "file open the save yuv file:" << yuv_output_path << std::endl;
                        std::cerr << "error code:" << errno << "; with msg:" << strerror(errno) << std::endl;
                    }
                }
                //
            }
            av_packet_unref(&packet);
        }
        // 清理资源
        av_frame_free(&p_frame);
        sws_freeContext(sws_context);
        avformat_close_input(&input_format_context);
        avcodec_free_context(&avcodec_context);
        std::cout << "finish ConvertY4mToYuv" << std::endl;
    }

    void ToolFfmpeg::ConvertYuvToJpgPng(const std::string &yuvPath, const std::string &outputFile)
    {
        std::cout << "begin ConvertYuvToJpgPng..." << std::endl;
        int width = 352;
        int height = 288;

        std::ifstream inputFile(yuvPath, std::ios::binary);
        if (!inputFile)
        {
            std::cerr << "Failed to open YUV file: " << yuvPath << std::endl;
            return;
        }

        // 计算 YUV 数据的大小
        int yuvSize = width * height + (width / 2) * (height / 2) * 2;

        // 创建缓冲区来存储 YUV 数据
        std::vector<uint8_t> yuvData(yuvSize);

        // 从文件中读取 YUV 数据
        inputFile.read(reinterpret_cast<char *>(yuvData.data()), yuvSize);

        if (!inputFile)
        {
            std::cerr << "Failed to read YUV data from file: " << yuvPath << std::endl;
            return;
        }
        cv::Mat yuvImg(height + height / 2, width, CV_8UC1, const_cast<uint8_t *>(yuvData.data()));
        cv::Mat bgrImg;
        cv::cvtColor(yuvImg, bgrImg, cv::COLOR_YUV2BGR_I420);

        std::vector<int> params;
        params.push_back(cv::IMWRITE_JPEG_QUALITY);
        params.push_back(100); // JPEG 压缩质量
        cv::imwrite(outputFile, bgrImg, params);
        std::cout << "finish yuvToPngJpg." << std::endl;
    }
} // namespace tool::ffmpeg
